//基于crypto-js进行AES加密,如果没有就先安装:npm i --save crypto-js
import CryptoJS from 'crypto-js';
//使用jsencrypt进行RSA加密,如果没有就先安装:npm i --save jsencrypt.官网:https://github.com/travist/jsencrypt
import {JSEncrypt} from 'jsencrypt';

/**
 * 加解密工具类:支持RSA、AES加解密与签名
 * 参考文档：https://blog.csdn.net/zziamalei/article/details/80090370
 */
export class ToolEncode {
  /**
   * RSA加密
   * @param data 需要加密的数据
   * @param key 密钥
   * @param type 加密类型,为0、1或2,默认为0。
   * @returns 加密后的数据
   */
  encodeRSA(data, key, type = 0) {
    let encryptTool = new JSEncrypt();
    encryptTool.setPublicKey(key);

    if (type === 0) {
      // 方式一
      return encryptTool.encrypt(data);
    } else if (type === 1) {
      // 方式二
      return encryptTool.getKey().encrypt(data);
    } else {
      /**
       * 方式三：先RSA加密，然后base64转码，然后转16进制。
       * 虽然加密结果与方式二的加密结果不同，但是解密与方式二的一样，即通过encryptTool.getKey().decrypt()解密。
       */
      return new Buffer(encryptTool.encrypt(data), 'base64').toString('hex');
    }
  }

  /**
   * RSA解密
   * @param data 需要解密的数据
   * @param key 密钥
   * @param type 解密类型,为0,1,默认为0.
   * @returns 解密后的数据
   */
  decodeRSA(data, key, type = 0) {
    let encryptTool = new JSEncrypt();
    encryptTool.setPrivateKey(key);

    if (type === 0) {
      return encryptTool.decrypt(data);
    } else {
      return encryptTool.getKey().decrypt(data);
    }
  }

  /**
   * 获取默认的RSA密钥,用于测试
   * @param type 密钥类型,0为公钥,1为私钥
   */
  getKeyRSADefault(type = 0) {
    //模拟公钥
    let PUBLIC_KEY =
      'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDPkeBIRYxiWa69N81lUQokdXXv' +
      'KzCbs9o9ZycJkVtUeg2LxSjYsELxfgmeuW4OC/vjnI6Mz4JhP+ps3CPlVbU+um+/' +
      'rk5aYhqmOnMOlyfwNgLhPyb5pw8MDQ9Zm60lxgwgsSvh5uVgwtMP1I8yqbx4mVQG' +
      'OcyUQGZXzYJ564ykaQIDAQAB';
    //模拟私钥
    let PRIVATE_KEY =
      'MIICeQIBADANBgkqhkiG9w0BAQEFAASCAmMwggJfAgEAAoGBAM+R4EhFjGJZrr03' +
      'zWVRCiR1de8rMJuz2j1nJwmRW1R6DYvFKNiwQvF+CZ65bg4L++OcjozPgmE/6mzc' +
      'I+VVtT66b7+uTlpiGqY6cw6XJ/A2AuE/JvmnDwwND1mbrSXGDCCxK+Hm5WDC0w/U' +
      'jzKpvHiZVAY5zJRAZlfNgnnrjKRpAgMBAAECgYEAgofM0AYUcyDMtycpXiZpR2Yq' +
      'ZN33MmMWwfhlXynukiAy9otk1Wd6oZzN9efMIr88hvVD257+8aMdC5ogZK7SS13d' +
      'QVqFVMi+APa0CH9gxs8/6kH4PWrLf3M/NnAPuvQjxuGSnVMk/YDF2EhjaPDW5KAA' +
      'UaVpbTUFU59Mva8w2IECQQD+l8zzzvFLdhoA2z2/IbaCD1yc1NXIJcCn+ChZMz86' +
      '/MxEfrMF2v+2Lbtg6A+/2d7rVGyIptcvLJ7CwpHAg8+5AkEA0LeMA6w091f6kfp2' +
      'I5fkql/m1zdNr+7qHbiVwx43fnbgp/nSqA2WJEXQMs4rEWRlTDJW00+Z4b8lCi0I' +
      'cB3yMQJBAJZIyWUKPYmL96GVyYcvwWpY4xR41pFFJ8p/RGITTDnnzDSKjebg9Z12' +
      'tWto0RMw6tQDi+XGhrOEJVtWO29m35ECQQCQ0Zpr/fPuX3Q0D8YjIkR+VvaUBAJ6' +
      'fVqibq4zaWb178QnqTcadfrkhjBU4CqP8co7mX4XNtc+BOPNcLu3F4AxAkEAg2iF' +
      '8C2b6UEojYtTUqOf2LQN7HIRtUwVacTU8deFCHLPxpFWpyjn/0XOT3frV9X5pNa0' +
      'Fhka3B6n2yGH75Bqbg==';

    if (type === 0) {
      return '-----BEGIN PUBLIC KEY-----' + PUBLIC_KEY + '-----END PUBLIC KEY-----';
    } else {
      return '-----BEGIN RSA PRIVATE KEY-----' + PRIVATE_KEY + '-----END RSA PRIVATE KEY-----';
    }
  }

  /**
   * AES加密
   * 转Utf8编码: CryptoJS.enc.Utf8.parse();
   * 转Base64: CryptoJS.enc.Base64.stringify();
   * @param data 需要加密的数据
   * @param key 密钥
   * @returns 加密后的数据
   */
  encodeAES(data, key) {
    if (typeof data !== 'string') {
      data = JSON.stringify(data);
    }

    //AES加密
    let result = CryptoJS.AES.encrypt(data, CryptoJS.enc.Utf8.parse(key), {
      iv: CryptoJS.enc.Utf8.parse('1234567812345678'), //向量。使用CBC模式时，需要使用向量。
      mode: CryptoJS.mode.CBC,
      padding: CryptoJS.pad.ZeroPadding //偏移量。使用非补码方式时，需要使用ZeroPadding。默认为PKCS5Padding。
    });
    //base64转码
    return CryptoJS.enc.Base64.stringify(result.ciphertext);
  }

  /**
   * AES解密
   * @param data 需要解密的数据
   * @param key 密钥
   * @returns 解密后的数据
   */
  decodeAES(data, key) {
    if (typeof data !== 'string') {
      data = JSON.stringify(data);
    }

    let result = CryptoJS.AES.decrypt(data, CryptoJS.enc.Utf8.parse(key), {
      iv: CryptoJS.enc.Utf8.parse('1234567812345678'), //向量。使用CBC模式时，需要使用向量。
      mode: CryptoJS.mode.CBC,
      padding: CryptoJS.pad.ZeroPadding //偏移量。使用非补码方式时，需要使用ZeroPadding。默认为PKCS5Padding。
    });
    //转为utf-8编码
    return CryptoJS.enc.Utf8.stringify(result);
  }

  /**
   * 随机生成16位的AES密钥
   */
  getKeyAES() {
    let key = [];
    for (let i = 0; i < 16; i++) {
      let num = Math.floor(Math.random() * 26);
      let charStr = String.fromCharCode(97 + num);
      key.push(charStr.toUpperCase());
    }
    let result = key.join('');
    return result;
  }

  /**
   * 签名,支持MD5签名与SHA1签名
   * @param data 需要签名的数据
   * @param key SHA1签名的密钥
   * @param type 签名类型,结果为0或1：0代表MD5签名,1代表SHA1签名,默认为0。
   */
  signature(data, key = '', type = 0) {
    let params = '';
    if (type === 0) {
      //先对data排序，然后拼接字符串
      Object.keys(data)
        .sort()
        .forEach((item) => {
          params += `${item}=${data[item]}, `;
        });
      params = params.slice(0, -2);
      //SHA256签名：使用CryptoJS.SHA256(),先将SHA256加密,然后转Hex的16进制
      return CryptoJS.SHA256(`{${params}}`).toString(CryptoJS.enc.Hex);
    } else {
      //先对data排序，然后拼接字符串
      Object.keys(data)
        .sort()
        .forEach((item) => {
          params += `${item}:${data[item]}|`;
        });
      params = params.slice(0, -1);
      //SHA1签名：使用CryptoJS.HmacSHA1(),先将SHA1加密,然后转base64
      return CryptoJS.HmacSHA1(`(${params})`, key).toString(CryptoJS.enc.Base64);
    }
  }

  /**
   * 加密登录密码等
   * 先进行SHA1加密，然后进行base64转码，然后使用RSA加密
   * @param value 需要加密的数据
   * @param key 密钥
   * @param time 获取RSA公钥时返回的时间
   */
  encodeLogin(value, key, time) {
    //应用渠道号
    let channel = '30001';
    time = time || new Date().getTime();
    let key_md5 = CryptoJS.MD5(channel).toString();
    value = CryptoJS.HmacSHA1(value, key_md5).toString(CryptoJS.enc.Base64);
    value = value + '$CurTime=' + time;

    return this.encodeRSA(value, key, 1);
  }

  /**
   * 创建唯一标识符
   */
  UUID() {
    let s = [];
    let hexDigits = '0123456789abcdef';
    for (let i = 0; i < 36; i++) {
      s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
    }
    s[14] = '4'; // bits 12-15 of the time_hi_and_version field to 0010
    s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01
    s[8] = s[13] = s[18] = s[23] = '-';

    let uuid = s.join('') + '-' + new Date().getTime();
    return uuid;
  }
}

export default new ToolEncode();
